package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.pojo.param.GunStatusUpdateParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.order.Job.DeviceCheckJob;
import cn.tedu.charging.order.RabbitMq.RabbitMQOrderProducer;
import cn.tedu.charging.order.constant.Constant;
import cn.tedu.charging.order.dao.repository.OrderRepository;
import cn.tedu.charging.order.feign.CostClient;
import cn.tedu.charging.order.feign.DeviceClient;
import cn.tedu.charging.order.feign.UserClient;
import cn.tedu.charging.order.mqtt.MqttProducer;
import cn.tedu.charging.order.pojo.dto.CarInfoDto;
import cn.tedu.charging.order.pojo.dto.ChargingDto;
import cn.tedu.charging.order.pojo.dto.StationInfoDto;
import cn.tedu.charging.order.pojo.param.ChargingResultParam;
import cn.tedu.charging.order.pojo.param.OrderAddParam;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.tedu.charging.order.pojo.po.OrderMQPO;
import cn.tedu.charging.order.pojo.vo.OrderDetailVO;
import cn.tedu.charging.order.service.OrderService;
import cn.tedu.charging.common.pojo.vo.StationInfoVO;
import cn.tedu.charging.common.pojo.vo.UserInfoVO;
import cn.tedu.charging.common.web.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Order;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.Random;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private UserClient userClient;
    @Autowired
    private DeviceClient deviceClient;
    @Autowired
    private MqttProducer mqttProducer;
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private RabbitMQOrderProducer rabbitMQOrderProducer;
    @Autowired
    private CostClient costClient;



    @Override
    public String createOrder(OrderAddParam orderAddParam) {
        //TODO 订单擦混关键的流程 自定向下变成
        //创建订单编号
        String orderNo = getOrderNo();
        log.debug("生成订单号:{}",orderNo);

        log.debug("生成设备自检任务:orderNo={},gunid={}",orderNo,orderAddParam.getGunId());
        DeviceCheckJob job = checkJob(orderNo,orderAddParam.getGunId());
        log.debug("生成自检任务成功,出参:{}",job);


        //通过枪id获取运营商信息和充电站信息
        log.debug("获取运营商和场站信息-入参:{}",orderAddParam.getGunId());
        StationInfoDto stationInfo = getStationInfo(orderAddParam.getGunId());
        log.debug("获取运营商和场站信息-入参:{},出参:{}",orderAddParam.getGunId(),stationInfo);

        //通过用户id获取与用户车辆信息
        log.debug("获取用户车辆信息 carInfoDto :{}",orderAddParam.getUserId());
        CarInfoDto carInfoDto = getCarInfo(orderAddParam.getUserId());
        log.debug("获取用户车辆信息-入参:{},出参:{}",orderAddParam.getUserId(),carInfoDto);

        //通过枪的id修改抢的状态为已使用
        log.debug("通过枪的id修改抢的状态-入参信息: gunid = {},状态 ={}",orderAddParam.getGunId(),Constant.GUN_STATUS_BUSY);
        Boolean success = updateGunStatus(orderAddParam.getGunId(),Constant.GUN_STATUS_BUSY);
        log.debug("通过枪的id修改抢的状态-出参信息: gunid = {},状态 ={},出参={}",orderAddParam.getGunId(),Constant.GUN_STATUS_BUSY,success);

        //处理异常
        if (!success){
            log.warn("修改抢的状态失败,枪id:{},状态:{}",orderAddParam.getGunId(),Constant.GUN_STATUS_BUSY);
        }
        //5.给充电桩发送充电指令
        startCharging(orderNo,orderAddParam.getPileId(),orderAddParam.getGunId());

        /*
        * 6.开始充电后会出现三种数据情况
                正常情况 设备会定时和订单服务同步数据,温度,电压,用电度数
                异常情况 设备接收到开始充电指令后,开始充电,充了一会,设备故障,不会再和订单服务同步数据
                订单需要处理这种数据
                1 延迟消息  rabbit-mq 今天晚上要把rabbit-mq安装成功,
                2 定时任务
        * */

        //处理延迟异常
        sendLatencyAnomalies(orderNo);
        return orderNo;
    }

    //生成自检任务异常处理
    private DeviceCheckJob checkJob(String orderNo,Integer GunId) {
        DeviceCheckJob job;
        try {
           job  = new DeviceCheckJob(orderNo,GunId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return job;
    }

    //处理延迟异常
    private void sendLatencyAnomalies(String ordedrNo) {
        OrderMQPO orderMQPO = new OrderMQPO();
        orderMQPO.setOrderNo(ordedrNo);
        //处理延迟异常的入参
        rabbitMQOrderProducer.sendOrder(orderMQPO);
    }


    //创建成功订单信息
    @Override
    public void saveSuccessOrder(ChargingResultParam resultParam) {
        ChargingBillSuccessPO successPO = new ChargingBillSuccessPO();
        successPO.setBillId("1");
        orderRepository.saveSuccessOrder(successPO);
    }


    //停止充电业务
    @Override
    public OrderDetailVO normalStop(String orderNo) {
        //通过OrderNo获取创建订单的详细信息
        ChargingBillSuccessPO chargingBillSuccessPO = getOrderInfoByOrderNo(orderNo);
        //service查询数据都要哦安短是否为空,防止空指针异常
        if (chargingBillSuccessPO != null){
            //获取订单花销(用户的消费金额)
            ChargingProcessVO chargingProcessResult = getOrderCostByOrderNo(orderNo);
            if ( chargingProcessResult !=null){
                //TODO 给充电桩发送停止充电指令
                //更新订单状态为停止充电
                updateOrderStatus2NormalStop(orderNo);
                //获取用户最新的余额(用户的余额)
                BigDecimal userBalance = getUserBalance(chargingBillSuccessPO.getUserId());
                //计算用户剩余的金额(应该扣的钱  用户余额 -  消费金额)
                BigDecimal currentBalance = userBalance.subtract(chargingProcessResult.getTotalCost());
                //返回扣除当前订单话费的余额
                OrderDetailVO orderDetailVO= buildOrderDetailVO(orderNo,currentBalance,chargingProcessResult);
                return orderDetailVO;
            }else{
                //todo 自定义异常,订单获取订单价格失败
            }
        }else {
            //todo 自定义异常 订单不存在
        }
        return new OrderDetailVO();
    }

    //构建正常停止充电返回得到结果
    private OrderDetailVO buildOrderDetailVO(String orderNo, BigDecimal currentBalance, ChargingProcessVO chargingProcessResult) {
        OrderDetailVO orderDetailVO = new OrderDetailVO();
        orderDetailVO.setOrderNo(orderNo);
        orderDetailVO.setBalance(currentBalance);
        orderDetailVO.setTotalCost(chargingProcessResult.getTotalCost());
        orderDetailVO.setHours(chargingProcessResult.getHours());
        orderDetailVO.setMinutes(chargingProcessResult.getMinutes());
        orderDetailVO.setSeconds(chargingProcessResult.getSeconds());
        return orderDetailVO;
    }

    //根据订单Id获取订单消费金额
    private ChargingProcessVO getOrderCostByOrderNo(String orderNo) {
        JsonResult<ChargingProcessVO> costResult =  costClient.getCostByOrderNo(orderNo);
        if (costResult != null) {
            return costResult.getData();
        }
        return null;
    }

    //获取用户的最新的余额
    private BigDecimal getUserBalance(Integer userId) {
        log.debug("调用用户服务获取用户余额:{}",userId);
        JsonResult<BigDecimal> userBalance = userClient.getUserBalance(userId);
        log.debug("调用用户服务获取用户余额:入参:{},出参:{}",userId,userBalance);
        if (userBalance!=null){
            return userBalance.getData();
        }
        return new BigDecimal(0);
    }

    //更新订单状态为停止充电
    private void updateOrderStatus2NormalStop(String orderNo) {
        orderRepository.updateOrderStatus2NormalStop(orderNo);
    }

    //通过OrderNo获取创建订单的详细信息
    private ChargingBillSuccessPO getOrderInfoByOrderNo(String orderNo) {
        return orderRepository.getSuccess(orderNo);
    }

    //通过随机算法实现负载均衡
    public String chooseUrl(){
        //模拟拥有三个服务器
        String url = "http://localhost:8083/user/info/{1}"; //占位符号
        String url1 = "http://localhost:8084/user/info/{1}"; //占位符号
        String url2 = "http://localhost:8085/user/info/{1}"; //占位符号

        String[] servers = new String[3];
        servers[0] = url;
        servers[1] = url1;
        servers[2] = url2;

        Random random = new Random();
        int r = random.nextInt(servers.length);
        String server = servers[r];
        return server;
    }

    //通过用户Id获取绑定的车辆信息
    public CarInfoDto getCarInfo(Long userId){
        return getCarInfoByFeign(userId);
    }

    private CarInfoDto getCarInfoByRestTemplate(Integer userId) {
        String url = chooseUrl();
        log.debug("入参的url :{}",url);
        log.debug("通过restTemplate调用用户接口获取用户信息-入参:{}",userId);

        //参数类型转化  Parameter(参数)  Reference(转化)
        ParameterizedTypeReference<JsonResult<UserInfoVO>>  responseBody =
                new ParameterizedTypeReference<JsonResult<UserInfoVO>>() {
                };
        ResponseEntity<JsonResult<UserInfoVO>> entity = restTemplate.exchange(url, HttpMethod.GET,null,responseBody,userId);
        log.debug("通过restTemplate调用用户接口获取用户信息-入参:{},出参:{}",userId,entity);
        CarInfoDto carInfoDto = new CarInfoDto();
        if (entity !=null){
            HttpStatus httpStatus = entity.getStatusCode();
            if (HttpStatus.OK == httpStatus) {
                JsonResult<UserInfoVO> body = entity.getBody();
                log.debug("通过restTemplate调用用户接口获取用户信息-入参:{},body:{}", userId, body);
                UserInfoVO data = body.getData();
                //springboot 默认使用的jackson
                // ObjectMapper = new ObjectMapper();
                // Obj转化为字符串的三种方式
                // 1. String string = result.toString();
                // 2. String str  = result+ "";
                // 3.String str = String.valueOf(result);
                Integer carId = data.getCarId();
                //BeanUtils 是 源数据 传递到 目标数据
                BeanUtils.copyProperties(data,carInfoDto);
            }
        }
        return carInfoDto;
    }

    //异步下发充电指令给充电桩
    private void startCharging(String orderNo,Integer pileId,Integer gunId) {
        log.debug("发送指令:订单号:{},桩id:{},枪id:{}", orderNo, pileId, gunId);
        ChargingDto chargingDto = new ChargingDto();
        chargingDto.setGunId(gunId);
        chargingDto.setPileId(pileId);
        chargingDto.setMsg(Constant.START_CHARGING);
        chargingDto.setOrderNo(orderNo);
        log.debug("发送指令-入参: {}", chargingDto);
        mqttProducer.send(chargingDto);
    }

    //更新枪的状态
    private Boolean updateGunStatus(Integer gunId, Integer status) {
        GunStatusUpdateParam param = new GunStatusUpdateParam();
        param.setId(gunId);
        param.setStatus(status);
        JsonResult<Boolean> jsonResult = deviceClient.updateGunStatusParam(param);
        if (jsonResult !=null){
            return jsonResult.getData();
        }
        return false;
    }

    //通过Feign的方式调用用户服务
    private  CarInfoDto getCarInfoByFeign(Long userId){
        JsonResult<UserInfoVO> userInfo = userClient.getUserInfo(userId);
        if (userInfo != null){
            UserInfoVO data = userInfo.getData();
            if (data != null){
                CarInfoDto carInfoDto = new CarInfoDto();
                BeanUtils.copyProperties(data,carInfoDto);
                return carInfoDto;
            }
        }
        return null;
    }


    //通过gunId获取运营商信息和充电桩信息(OpenFeign的方式)
    private StationInfoDto getStationInfo(Integer gunId) {
        JsonResult<StationInfoVO> station = deviceClient.createStation(gunId);
        if (station!=null){
            StationInfoVO data = station.getData();
            if (data!=null){
                StationInfoDto stationInfoDto = new StationInfoDto();
                BeanUtils.copyProperties(data,stationInfoDto);
                return stationInfoDto;
            }
        }
        return null;
    }

    //生成订单号
    private String getOrderNo(){
        String start =  "10000";
        long now = System.currentTimeMillis();
        Random random = new Random();
        int r = random.nextInt(100);
        return start + "_" + r + "_" + now;
    }
}
